package com.hmdp.utils;

import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;

import static com.hmdp.utils.RedisConstants.CACHE_SHOP_KEY;

@Slf4j
@Component
public class CacheClient {
    /**
     * 缓存工具类封装
     */
    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    //定义四个方法，两个set两个get

    //设置TTL过期时间
    public void set(String key, Object value, Long time, TimeUnit unit){
        //Object对象转json是序列化一种啊
        String jsonStr = JSONUtil.toJsonStr(value);//你点这个toJsonStr的参数查看可以知道参数是Object，所以是序列化操作
        stringRedisTemplate.opsForValue().set(key,JSONUtil.toJsonStr(value),time,unit);
    }

    //设置逻辑过期时间解决缓存击穿
    public void setLogicExpire(String key, Object value, Long time, TimeUnit unit) {
        RedisData redisdata = new RedisData();
        redisdata.setData(value);
        redisdata.setExpireTime(LocalDateTime.now().plusSeconds(unit.toSeconds(time)));
        stringRedisTemplate.opsForValue().set(key, JSONUtil.toJsonStr(redisdata));
    }

    //缓存空值解决缓存穿透
    public <R,IDtype> R getwithPassThrough(String keyPrefix, IDtype ID, Class<R> type, Function<IDtype,R> dbFallBack,Long time,TimeUnit unit){
        String key = keyPrefix + ID;
        String jsonStr = stringRedisTemplate.opsForValue().get(key);
        if (StrUtil.isNotBlank(jsonStr)) {
            return JSONUtil.toBean(jsonStr,type);//根据key查询redis缓存，查出来返回的东西 反序列化为指定类型type
        }
        if(jsonStr.equals("")){ //如果是空命中
            return null;
        }
        //函数式编程，在方法的形参列表中传递这个函数
        //使用有参有返回值的函数--Function
        R dbfallback = dbFallBack.apply(ID);
//        4.数据库查不到，为了防止缓存击穿，先缓存一个null值再返回错误信息
        if (dbfallback==null) {
            stringRedisTemplate.opsForValue().set(key,"",RedisConstants.CACHE_NULL_TTL,TimeUnit.MINUTES);//空值的ttl就设置个短一点的过期时间
            return null;
        }
//        5.数据库查得到，写回redis;写缓存的时候添加ttl
        this.set(key,dbfallback,time,unit);
//        6.返回商户
        return dbfallback;
    }

    //查询时逻辑过期解决缓存击穿



}
